package study.itwill.common;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

import org.apache.log4j.Logger;

public class DBHandler {
	
	// Logger 클래스의 인스턴스를 받아온다.
	static Logger logger = Logger.getLogger(DBHandler.class);

	/** 접속 정보 값 */
	private static String HOSTNAME = null;
	private static int HOSTPORT = 0;
	private static String DATABASE = null;
	private static String USERNAME = null;
	private static String PASSWORD = null;
	private static String CHARSET = null;

	/** 접속에 필요한 객체 */
	private static Connection con = null;
	private static Statement stmt = null;

	/** 접속 여부 체크 값 */
	private static boolean isConnect = false;

	/**
	 * 접속 정보를 설정한다.
	 * 
	 * @param p1
	 *            서버의 주소 (IP or domain)
	 * @param p2
	 *            서버의 포트번호
	 * @param p3
	 *            데이터베이스 이름
	 * @param p4
	 *            사용자 아이디
	 * @param p5
	 *            비밀번호
	 * @param p6
	 *            인코딩셋 (UTF8 or EUCKR)
	 */
	public static void setInfo(String p1, int p2, String p3, String p4,
			String p5, String p6) {
		
		logger.debug("=========== method start >> setInfo");
		logger.debug("HOSTNAME: " + p1);
		logger.debug("HOSTPORT: " + p2);
		logger.debug("DATABASE: " + p3);
		logger.debug("USERNAME: " + p4);
		logger.debug("PASSWORD: " + p5);	// 보안을 이유로, 비밀번호는 잘 안남긴다.
		logger.debug("CHARSET: " + p6);
		
		HOSTNAME = p1;
		HOSTPORT = p2;
		DATABASE = p3;
		USERNAME = p4;
		PASSWORD = p5;
		CHARSET = p6;
		
		logger.debug("=========== method end >> setInfo");
	}

	/**
	 * DATABASE에 접속한다.
	 * 
	 * @return boolean 접속 성공 여부
	 */
	public static boolean connect() {
		
		logger.debug("=========== method start >> connect");
		
		// 중복 접속 방지
		if (isConnect) {
			return isConnect;
		}

		try {
			Class.forName("com.mysql.jdbc.Driver");
			String conString = "jdbc:mysql://%s:%d/%s?" + "user=%s&"
					+ "password=%s&" + "characterEncoding=%s";

			String info = String.format(conString, HOSTNAME, HOSTPORT,
					DATABASE, USERNAME, PASSWORD, CHARSET);

			con = DriverManager.getConnection(info);
			stmt = con.createStatement();

			// 접속 성공시 상태값 변경
			isConnect = true;
		} catch (ClassNotFoundException e) {
			logger.error("에러 >> JDBC 드라이버 로드 실패");
			logger.error(e.getLocalizedMessage());
			logger.error(e);
		} catch (SQLException e) {
			logger.error("에러 >> 데이터베이스 접속 실패");
			logger.error(e.getLocalizedMessage());
			logger.error(e);
		} catch (Exception e) {
			logger.error("에러 >> 알 수 없는 에러 발생함");
			logger.error(e.getLocalizedMessage());
			logger.error(e);
		}
		
		logger.debug("=========== method end >> connect");

		return isConnect;
	}

	/**
	 * INSERT, UPDATE, DELETE 쿼리를 수행한 한다.
	 * 
	 * @param sql
	 *            수행할 SQL문
	 * @return boolean 쿼리 수행 성공 여부
	 */
	public static boolean execSql(String sql) {
		
		logger.debug("=========== method start >> execSql");
		logger.debug("[SQL] " + sql);
		
		boolean result = false;

		if (isConnect) {
			try {
				stmt.executeUpdate(sql);
				result = true;
			} catch (SQLException e) {
				logger.error("에러 >> SQL문 오류");
				logger.error(e.getLocalizedMessage());
				logger.error(e);
			} catch (Exception e) {
				logger.error("에러 >> 알 수 없는 에러 발생함");
				logger.error(e.getLocalizedMessage());
				logger.error(e);
			}
		} else {
			logger.error("에러 >> 접속중이지 않음");
		}
		
		logger.debug("=========== method end >> execSql");

		return result;
	}
	
	public static boolean execSql(String target, Object... args) {
		String sql = String.format(target, args);
		return execSql(sql);
	}

	/**
	 * 접속을 해제한다.
	 */
	public static void disconnect() {
		
		logger.debug("=========== method start >> disconnect");
		
		if (isConnect) {
			try {
				stmt.close();
				stmt = null;
			} catch (Exception e) {
				logger.error(e);
			}

			try {
				con.close();
				con = null;
			} catch (Exception e) {
				logger.error(e);
			}

			// 상태변경
			isConnect = false;
		}
		
		logger.debug("=========== method end >> disconnect");
	}

	/**
	 * 트렌젝션을 시작한다.
	 */
	public static void beginTrans() {
		
		logger.debug("=========== method start >> beginTrans");
		
		try {
			con.setAutoCommit(false);
		} catch (SQLException e) {
			logger.error("트렌젝션 시작 실패 >> " 
						+ e.getLocalizedMessage());
			logger.error(e);
		}
		
		logger.debug("=========== method end >> beginTrans");
	}

	/**
	 * 트렌젝션을 종료하고 커밋한다.
	 */
	public static void commit() {
		
		logger.debug("=========== method start >> commit");
		
		try {
			con.commit();
		} catch (SQLException e) {
			logger.error("커밋 실패 >> " 
					+ e.getLocalizedMessage());
			logger.error(e);
		}
		
		logger.debug("=========== method end >> commit");
	}

	/**
	 * 트렌젝션을 종료하고 롤백한다.
	 */
	public static void rollback() {
		
		logger.debug("=========== method start >> rollback");
		
		try {
			con.rollback();
		} catch (SQLException e) {
			logger.error("롤백 실패 >> " 
					+ e.getLocalizedMessage());
			logger.error(e);
		}
		
		logger.debug("=========== method end >> rollback");
	}

	/**
	 * private로 선언되어 있는 Statement 객체를 리턴
	 */
	public static Statement getStatement() {
		
		logger.debug("=========== method start >> getStatement");
		
		if (!isConnect) {
			return null;
		}
		
		logger.debug("=========== method end >> getStatement");

		return stmt;
	}

	/**
	 * SELECT 쿼리를 수행하고 ResultSet을 반환한다.
	 * 
	 * @param sql
	 *            수행할 SQL문
	 * @return ResultSet 결과를 담고 있는 객체
	 */
	public static ResultSet getResult(String sql) {
		
		logger.debug("=========== method start >> getResult");
		logger.debug("[SQL] " + sql);
		
		ResultSet result = null;

		if (isConnect) {
			try {
				result = stmt.executeQuery(sql);
			} catch (SQLException e) {
				logger.error("에러 >> SQL문 오류");
				logger.error(e.getLocalizedMessage());
				logger.error(e);
			} catch (Exception e) {
				logger.error("에러 >> 알 수 없는 에러 발생함");
				logger.error(e.getLocalizedMessage());
				logger.error(e);
			}
		}
		
		logger.debug("=========== method end >> getResult");

		return result;
	}
	
	public static ResultSet getResult(String target, Object... args) {
		String sql = String.format(target, args);
		return getResult(sql);
	}

	/**
	 * DB에게 COUNT함수를 사용한 질의 결과를 받아 리턴한다.
	 * 
	 * @param table
	 *            테이블 이름
	 * @param column
	 *            COUNT함수를 적용할 컬럼 이름
	 * @param where
	 *            WHERE절 (필요한 경우 사용)
	 * @return 조건에 맞는 데이터의 수
	 */
	public static int getCount(String table, String column,
			String where) {

		logger.debug("=========== method start >> getCount");
		
		int result = 0;

		String template = "SELECT COUNT(%s) FROM %s";
		String sql = String.format(template, column, table);

		if (where != null) {
			sql += " WHERE " + where;
		}

		ResultSet rs = getResult(sql);

		if (rs != null) {
			try {
				rs.next();
				result = rs.getInt(1);
			} catch (SQLException e) {
				logger.error("COUNT처리 에러");
				logger.error(e.getLocalizedMessage());
				logger.error(e);
			} finally {
				try {
					rs.close();
				} catch (SQLException e) {
					logger.error(e);
				}
				rs = null;
			}
		}
		
		logger.debug("=========== method end >> getCount");

		return result;

	}

	/**
	 * DB에게 MAX함수를 사용한 질의 결과를 받아 리턴한다.
	 * 
	 * @param table
	 *            테이블 이름
	 * @param column
	 *            COUNT함수를 적용할 컬럼 이름
	 * @param where
	 *            WHERE절 (필요한 경우 사용)
	 * @return 조건에 맞는 데이터의 수
	 */
	public static int getMax(String table, String column, String where) {

		logger.debug("=========== method start >> getMax");
		
		int result = 0;

		String template = "SELECT MAX(%s) FROM %s";
		String sql = String.format(template, column, table);

		if (where != null) {
			sql += " WHERE " + where;
		}

		ResultSet rs = getResult(sql);

		if (rs != null) {
			try {
				rs.next();
				result = rs.getInt(1);
			} catch (SQLException e) {
				logger.error("MAX처리 에러");
				logger.error(e.getLocalizedMessage());
			} finally {
				try {
					rs.close();
				} catch (SQLException e) {
					logger.error(e);
				}
				rs = null;
			}
		}
		
		logger.debug("=========== method end >> getMax");

		return result;

	}

	/**
	 * DB에게 MIN함수를 사용한 질의 결과를 받아 리턴한다.
	 * 
	 * @param table
	 *            테이블 이름
	 * @param column
	 *            COUNT함수를 적용할 컬럼 이름
	 * @param where
	 *            WHERE절 (필요한 경우 사용)
	 * @return 조건에 맞는 데이터의 수
	 */
	public static int getMin(String table, String column, String where) {

		logger.debug("=========== method start >> getMin");
		
		int result = 0;

		String template = "SELECT MIN(%s) FROM %s";
		String sql = String.format(template, column, table);

		if (where != null) {
			sql += " WHERE " + where;
		}

		ResultSet rs = getResult(sql);

		if (rs != null) {
			try {
				rs.next();
				result = rs.getInt(1);
			} catch (SQLException e) {
				logger.error("MIN처리 에러");
				logger.error(e.getLocalizedMessage());
			} finally {
				try {
					rs.close();
				} catch (SQLException e) {
					logger.error(e);
				}
				rs = null;
			}
		}
		
		logger.debug("=========== method end >> getMin");

		return result;

	}
}
